{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "3275cb38-e063-4304-92da-b11337724bea",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T02:47:04.642005Z",
     "iopub.status.busy": "2024-08-10T02:47:04.641313Z",
     "iopub.status.idle": "2024-08-10T02:47:04.651963Z",
     "shell.execute_reply": "2024-08-10T02:47:04.649710Z",
     "shell.execute_reply.started": "2024-08-10T02:47:04.641956Z"
    }
   },
   "outputs": [],
   "source": [
    "from IPython.display import Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "e789c99e-81bb-41b4-9790-29a7aae1c77b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T03:22:18.541846Z",
     "iopub.status.busy": "2024-08-10T03:22:18.541224Z",
     "iopub.status.idle": "2024-08-10T03:22:18.554264Z",
     "shell.execute_reply": "2024-08-10T03:22:18.553030Z",
     "shell.execute_reply.started": "2024-08-10T03:22:18.541798Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "from dotenv import load_dotenv\n",
    "# LANGCHAIN_TRACING_V2=true\n",
    "# LANGCHAIN_API_KEY=\n",
    "# OPENAI_API_KEY=\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9331343b-ab49-4718-9346-9755de6fac82",
   "metadata": {},
   "source": [
    "## 认识 Concepts "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2084a255-cf2b-47dd-a2bc-9694405d8753",
   "metadata": {},
   "source": [
    "- 过程信息（所谓的对话式 agent，conversational agents，就是带记忆）\n",
    "    - stateless LLMs => with memory (maintaining previous interactions)\n",
    "    - intermediate_steps： list of actions/tools (input, output)\n",
    "        - agent_scratchpad（草稿本）：list of tool messages\n",
    "    - 第一次 query-resp 之后，才会有这些过程信息\n",
    "        - 第一次：`intermediate_steps: []`, `agent_scratchpad: \"\"`\n",
    "- chat history\n",
    "    - messages (openai 的 api)\n",
    "    - system, user, assistant, user assistant ...\n",
    "- langsmith: 监控所有的 messages (input, output)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e587c439-8623-4318-9541-0cf7f2df4403",
   "metadata": {},
   "source": [
    "## ReAct (Reasoning & Acting)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4662c1c-e720-4f65-92c0-5ee05569e9b0",
   "metadata": {},
   "source": [
    "- https://arxiv.org/abs/2210.03629\n",
    "- ReAct（经典、且general、应用广泛的 prompt）\n",
    "    - User query => (Thought -> Action (Action Input) -> Observation(Action Ouptut)) * N\n",
    "        - Action input => output, function decision & calling \n",
    "    - multi-step/hops reasoning/interacting\n",
    "    - 跟 function calling（LangChain 中的 tools）天然地适配；"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "ec9d86b0-3c0e-48af-bed4-9f84072a6ab7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T02:49:55.248708Z",
     "iopub.status.busy": "2024-08-10T02:49:55.248195Z",
     "iopub.status.idle": "2024-08-10T02:49:57.186007Z",
     "shell.execute_reply": "2024-08-10T02:49:57.184144Z",
     "shell.execute_reply.started": "2024-08-10T02:49:55.248670Z"
    }
   },
   "outputs": [],
   "source": [
    "from langchain import hub\n",
    "prompt = hub.pull(\"hwchase17/react\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "b51754aa-de71-419b-b064-58084a0b302a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T02:50:15.689394Z",
     "iopub.status.busy": "2024-08-10T02:50:15.688782Z",
     "iopub.status.idle": "2024-08-10T02:50:15.701291Z",
     "shell.execute_reply": "2024-08-10T02:50:15.699144Z",
     "shell.execute_reply.started": "2024-08-10T02:50:15.689350Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(langchain_core.prompts.prompt.PromptTemplate,\n",
       " PromptTemplate(input_variables=['agent_scratchpad', 'input', 'tool_names', 'tools'], metadata={'lc_hub_owner': 'hwchase17', 'lc_hub_repo': 'react', 'lc_hub_commit_hash': 'd15fe3c426f1c4b3f37c9198853e4a86e20c425ca7f4752ec0c9b0e97ca7ea4d'}, template='Answer the following questions as best you can. You have access to the following tools:\\n\\n{tools}\\n\\nUse the following format:\\n\\nQuestion: the input question you must answer\\nThought: you should always think about what to do\\nAction: the action to take, should be one of [{tool_names}]\\nAction Input: the input to the action\\nObservation: the result of the action\\n... (this Thought/Action/Action Input/Observation can repeat N times)\\nThought: I now know the final answer\\nFinal Answer: the final answer to the original input question\\n\\nBegin!\\n\\nQuestion: {input}\\nThought:{agent_scratchpad}'))"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(prompt), prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "b7e1fe02-4084-4d42-ab44-4a3777f0e3c7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T02:50:26.527383Z",
     "iopub.status.busy": "2024-08-10T02:50:26.526771Z",
     "iopub.status.idle": "2024-08-10T02:50:26.536803Z",
     "shell.execute_reply": "2024-08-10T02:50:26.534634Z",
     "shell.execute_reply.started": "2024-08-10T02:50:26.527338Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Answer the following questions as best you can. You have access to the following tools:\n",
      "\n",
      "{tools}\n",
      "\n",
      "Use the following format:\n",
      "\n",
      "Question: the input question you must answer\n",
      "Thought: you should always think about what to do\n",
      "Action: the action to take, should be one of [{tool_names}]\n",
      "Action Input: the input to the action\n",
      "Observation: the result of the action\n",
      "... (this Thought/Action/Action Input/Observation can repeat N times)\n",
      "Thought: I now know the final answer\n",
      "Final Answer: the final answer to the original input question\n",
      "\n",
      "Begin!\n",
      "\n",
      "Question: {input}\n",
      "Thought:{agent_scratchpad}\n"
     ]
    }
   ],
   "source": [
    "# 一个完整的 prompt 模板，历史对话/函数调用（输入输出）信息不断地追加在这一个 prompt 中，而不是维护成 messages list\n",
    "print(prompt.template)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe4c10a3-8e2f-4bb0-86ba-39de7658c110",
   "metadata": {},
   "source": [
    "- Thought: you should always think about what to do\n",
    "- Action: the action to take, should be one of `[{tool_names}]`\n",
    "- Action Input: the input to the action\n",
    "- Observation: the result of the action\n",
    "- ... (this Thought/Action/Action Input/Observation can repeat N times)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "691ee764-f083-43e0-ba46-f6618824f150",
   "metadata": {},
   "source": [
    "## LangChain projects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "5edf6017-f81f-4304-b20a-6be975030e9c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T02:47:12.217518Z",
     "iopub.status.busy": "2024-08-10T02:47:12.217105Z",
     "iopub.status.idle": "2024-08-10T02:47:12.227970Z",
     "shell.execute_reply": "2024-08-10T02:47:12.225954Z",
     "shell.execute_reply.started": "2024-08-10T02:47:12.217490Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"https://miro.medium.com/v2/resize:fit:1400/format:webp/1*bkNjqXR1LjAVJQbNLpfYvA.png\" width=\"500\"/>"
      ],
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image(url='https://miro.medium.com/v2/resize:fit:1400/format:webp/1*bkNjqXR1LjAVJQbNLpfYvA.png', width=500)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc127e1a-91c9-4fa7-bd7c-5da202960bfc",
   "metadata": {},
   "source": [
    "- AgentExecutor：agent that is using tools\n",
    "\n",
    "```\n",
    "next_action = agent.get_action(...)  \n",
    "while next_action != AgentFinish:\n",
    "  observation = run(next_action)\n",
    "  next_action = agent.get_action(..., next_action, observation)\n",
    "return next_action\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ca586579-eb43-4ad6-84ae-0879d649e2d8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T02:49:06.598632Z",
     "iopub.status.busy": "2024-08-10T02:49:06.597981Z",
     "iopub.status.idle": "2024-08-10T02:49:06.607108Z",
     "shell.execute_reply": "2024-08-10T02:49:06.604897Z",
     "shell.execute_reply.started": "2024-08-10T02:49:06.598585Z"
    }
   },
   "outputs": [],
   "source": [
    "os.environ[\"LANGCHAIN_PROJECT\"] = 'conversational_agents'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "20b46f27-251c-43c3-b230-539c9648b75d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T02:54:53.312698Z",
     "iopub.status.busy": "2024-08-10T02:54:53.311495Z",
     "iopub.status.idle": "2024-08-10T02:54:53.321720Z",
     "shell.execute_reply": "2024-08-10T02:54:53.319644Z",
     "shell.execute_reply.started": "2024-08-10T02:54:53.312635Z"
    }
   },
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "from langchain.tools import tool\n",
    "from langchain.agents import AgentExecutor, create_react_agent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "653cf960-7d69-402f-87b7-f6926b9d157e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T02:52:44.432838Z",
     "iopub.status.busy": "2024-08-10T02:52:44.432535Z",
     "iopub.status.idle": "2024-08-10T02:52:44.447385Z",
     "shell.execute_reply": "2024-08-10T02:52:44.445584Z",
     "shell.execute_reply.started": "2024-08-10T02:52:44.432818Z"
    }
   },
   "outputs": [],
   "source": [
    "@tool\n",
    "def get_employee_id(name):\n",
    "  \"\"\"\n",
    "  To get employee id, it takes employee name as arguments\n",
    "  name(str): Name of the employee\n",
    "  \"\"\"\n",
    "  fake_employees = {\n",
    "    \"Alice\": \"E001\", \"Bob\": \"E002\", \"Charlie\": \"E003\",\n",
    "    \"Diana\": \"E004\", \"Evan\": \"E005\", \"Fiona\": \"E006\",\n",
    "    \"George\": \"E007\", \"Hannah\": \"E008\", \"Ian\": \"E009\",\n",
    "    \"Jasmine\": \"E010\"\n",
    "  }\n",
    "  \n",
    "  return fake_employees.get(name, \"Employee not found\")\n",
    "\n",
    "# Custom tool for the Agent \n",
    "# ValueError: Function must have a docstring if description not provided.\n",
    "@tool\n",
    "def get_employee_salary(employee_id):\n",
    "  \"\"\"\n",
    "  To get the salary of an employee, it takes employee_id as input and return salary\n",
    "  \"\"\"\n",
    "  employee_salaries = {\n",
    "    \"E001\": 56000, \"E002\": 47000, \"E003\": 52000,\n",
    "    \"E004\": 61000, \"E005\": 45000, \"E006\": 58000,\n",
    "    \"E007\": 49000, \"E008\": 53000, \"E009\": 50000,\n",
    "    \"E010\": 55000\n",
    "    }\n",
    "  return employee_salaries.get(employee_id,\"Employee not found\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "1b8e985f-3f70-488d-8787-4adc1caf31f9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T02:56:13.040531Z",
     "iopub.status.busy": "2024-08-10T02:56:13.040094Z",
     "iopub.status.idle": "2024-08-10T02:56:18.166765Z",
     "shell.execute_reply": "2024-08-10T02:56:18.164543Z",
     "shell.execute_reply.started": "2024-08-10T02:56:13.040500Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mTo determine Evan's salary, I first need to obtain his employee ID.\n",
      "\n",
      "Action: get_employee_id\n",
      "Action Input: \"Evan\"\u001b[0m\u001b[33;1m\u001b[1;3mE005\u001b[0m\u001b[32;1m\u001b[1;3mNow that I have Evan's employee ID, I can retrieve his salary using this ID.\n",
      "\n",
      "Action: get_employee_salary\n",
      "Action Input: E005\u001b[0m\u001b[36;1m\u001b[1;3m45000\u001b[0m\u001b[32;1m\u001b[1;3mI now know the final answer.\n",
      "\n",
      "Final Answer: The salary of Evan is 45000.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'What is the Salary of Evan?',\n",
       " 'output': 'The salary of Evan is 45000.'}"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt = hub.pull(\"hwchase17/react\")\n",
    "llm = ChatOpenAI(model='gpt-4o')\n",
    "tools = [get_employee_salary, get_employee_id]\n",
    "agent = create_react_agent(llm, tools, prompt)\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n",
    "agent_executor.invoke({\"input\": \"What is the Salary of Evan?\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94a6d013-f069-4cde-b55e-dd59af61b064",
   "metadata": {},
   "source": [
    "### first query-resp"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c5f1e57d-823e-4962-b9fe-5b689bc927c4",
   "metadata": {},
   "source": [
    "LangSmith: https://smith.langchain.com/"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46a95050-6f24-4a78-9235-b2dea9fb253e",
   "metadata": {},
   "source": [
    " ```\n",
    "Answer the following questions as best you can. You have access to the following tools:\n",
    "\n",
    "    get_employee_salary(employee_id) - To get the salary of an employee, it takes employee_id as input and return salary\n",
    "    get_employee_id(name) - To get employee id, it takes employee name as arguments\n",
    "    name(str): Name of the employee\n",
    "\n",
    "    Use the following format:\n",
    "\n",
    "    Question: the input question you must answer\n",
    "    Thought: you should always think about what to do\n",
    "    Action: the action to take, should be one of [get_employee_salary, get_employee_id]\n",
    "    Action Input: the input to the action\n",
    "    Observation: the result of the action\n",
    "    ... (this Thought/Action/Action Input/Observation can repeat N times)\n",
    "    Thought: I now know the final answer\n",
    "    Final Answer: the final answer to the original input question\n",
    "\n",
    "    Begin!\n",
    "\n",
    "    Question: What is the Salary of Evan?\n",
    "    Thought:\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2099be65-c5b3-4aa2-bd92-2281a4897024",
   "metadata": {},
   "source": [
    "```\n",
    "To determine Evan's salary, I first need to obtain his employee ID.\n",
    "\n",
    "Action: get_employee_id\n",
    "Action Input: \"Evan\"\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "637b4ec6-f9f3-4f05-9ab7-1a2fec4cf46f",
   "metadata": {},
   "source": [
    "- get_employee_id\n",
    "    - input: Evan\n",
    "    - output: E005"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0bf9bf2-e703-40d7-a493-01714125ef5f",
   "metadata": {},
   "source": [
    "### second query-resp"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dca761dd-85e7-4197-a7bf-7916193dce6c",
   "metadata": {},
   "source": [
    "\n",
    "```\n",
    "Answer the following questions as best you can. You have access to the following tools:\n",
    "\n",
    "    get_employee_salary(employee_id) - To get the salary of an employee, it takes employee_id as input and return salary\n",
    "    get_employee_id(name) - To get employee id, it takes employee name as arguments\n",
    "    name(str): Name of the employee\n",
    "\n",
    "    Use the following format:\n",
    "\n",
    "    Question: the input question you must answer\n",
    "    Thought: you should always think about what to do\n",
    "    Action: the action to take, should be one of [get_employee_salary, get_employee_id]\n",
    "    Action Input: the input to the action\n",
    "    Observation: the result of the action\n",
    "    ... (this Thought/Action/Action Input/Observation can repeat N times)\n",
    "    Thought: I now know the final answer\n",
    "    Final Answer: the final answer to the original input question\n",
    "\n",
    "    Begin!\n",
    "\n",
    "    Question: What is the Salary of Evan?\n",
    "    Thought:To determine Evan's salary, I first need to obtain his employee ID.\n",
    "\n",
    "    Action: get_employee_id\n",
    "    Action Input: \"Evan\"\n",
    "    Observation: E005\n",
    "    Thought: \n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "251e527f-3509-4ca5-89b8-5f5338f767cc",
   "metadata": {},
   "source": [
    "```\n",
    "Now that I have Evan's employee ID, I can retrieve his salary using this ID.\n",
    "\n",
    "Action: get_employee_salary\n",
    "Action Input: E005\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "111536d3-5870-4d61-80f6-a4d786f79ccb",
   "metadata": {},
   "source": [
    "get_employee_salary\n",
    "- input: E005\n",
    "- output: 45000"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1703067a-6f51-4054-b9de-4601242e6e23",
   "metadata": {},
   "source": [
    "### finally"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68155dce-1065-4dbc-8703-572872958203",
   "metadata": {},
   "source": [
    "```\n",
    "Answer the following questions as best you can. You have access to the following tools:\n",
    "\n",
    "get_employee_salary(employee_id) - To get the salary of an employee, it takes employee_id as input and return salary\n",
    "get_employee_id(name) - To get employee id, it takes employee name as arguments\n",
    "name(str): Name of the employee\n",
    "\n",
    "Use the following format:\n",
    "\n",
    "Question: the input question you must answer\n",
    "Thought: you should always think about what to do\n",
    "Action: the action to take, should be one of [get_employee_salary, get_employee_id]\n",
    "Action Input: the input to the action\n",
    "Observation: the result of the action\n",
    "... (this Thought/Action/Action Input/Observation can repeat N times)\n",
    "Thought: I now know the final answer\n",
    "Final Answer: the final answer to the original input question\n",
    "\n",
    "Begin!\n",
    "\n",
    "Question: What is the Salary of Evan?\n",
    "Thought:To determine Evan's salary, I first need to obtain his employee ID.\n",
    "\n",
    "Action: get_employee_id\n",
    "Action Input: \"Evan\"\n",
    "Observation: E005\n",
    "Thought: Now that I have Evan's employee ID, I can retrieve his salary using this ID.\n",
    "\n",
    "Action: get_employee_salary\n",
    "Action Input: E005\n",
    "Observation: 45000\n",
    "Thought: \n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b768e058-4023-472f-85bd-e41a0090a21d",
   "metadata": {},
   "source": [
    "```\n",
    "I now know the final answer.\n",
    "\n",
    "Final Answer: The salary of Evan is 45000.\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "ef31c2b5-a417-4f5b-9268-e81effebc5c7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T03:25:36.767062Z",
     "iopub.status.busy": "2024-08-10T03:25:36.766454Z",
     "iopub.status.idle": "2024-08-10T03:25:36.778269Z",
     "shell.execute_reply": "2024-08-10T03:25:36.776583Z",
     "shell.execute_reply.started": "2024-08-10T03:25:36.767016Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"https://miro.medium.com/v2/resize:fit:1100/format:webp/0*j5T9A2o7AqQkk6lS\" width=\"500\"/>"
      ],
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image(url='https://miro.medium.com/v2/resize:fit:1100/format:webp/0*j5T9A2o7AqQkk6lS', width=500)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "713f6863-cd4c-44b9-b2f1-390b1b4ac4bf",
   "metadata": {},
   "source": [
    "### `显式地` 操作 agent_scratchpad 与 intermediate_steps"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1e82943-aeb2-4447-8dad-5df80c3cad25",
   "metadata": {},
   "source": [
    "https://python.langchain.com/v0.1/docs/modules/agents/how_to/custom_agent/\n",
    "- `format_to_openai_tool_messages`\n",
    "    - https://api.python.langchain.com/en/latest/_modules/langchain/agents/format_scratchpad/openai_tools.html#format_to_openai_tool_messages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "bb87ac4b-a6d5-4fc3-ad76-f20bff5da44f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T03:53:55.973236Z",
     "iopub.status.busy": "2024-08-10T03:53:55.972616Z",
     "iopub.status.idle": "2024-08-10T03:53:55.981777Z",
     "shell.execute_reply": "2024-08-10T03:53:55.979762Z",
     "shell.execute_reply.started": "2024-08-10T03:53:55.973190Z"
    }
   },
   "outputs": [],
   "source": [
    "os.environ[\"LANGCHAIN_PROJECT\"] = 'memory_agents'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "9a6b3678-87b0-4047-9336-a2fd4886540b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T03:54:03.970532Z",
     "iopub.status.busy": "2024-08-10T03:54:03.969903Z",
     "iopub.status.idle": "2024-08-10T03:54:03.996791Z",
     "shell.execute_reply": "2024-08-10T03:54:03.994634Z",
     "shell.execute_reply.started": "2024-08-10T03:54:03.970488Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "@tool\n",
    "def get_word_length(word: str) -> int:\n",
    "    \"\"\"Returns the length of a word.\"\"\"\n",
    "    return len(word)\n",
    "\n",
    "\n",
    "get_word_length.invoke(\"abc\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "03fd13da-825e-45c3-912a-5535074a1c22",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T03:57:47.759654Z",
     "iopub.status.busy": "2024-08-10T03:57:47.759007Z",
     "iopub.status.idle": "2024-08-10T03:57:47.767797Z",
     "shell.execute_reply": "2024-08-10T03:57:47.765717Z",
     "shell.execute_reply.started": "2024-08-10T03:57:47.759606Z"
    }
   },
   "outputs": [],
   "source": [
    "tools = [get_word_length]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "2d6d403a-5398-4300-9eab-7c5524b23d60",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T03:54:19.219127Z",
     "iopub.status.busy": "2024-08-10T03:54:19.218484Z",
     "iopub.status.idle": "2024-08-10T03:54:19.230604Z",
     "shell.execute_reply": "2024-08-10T03:54:19.228329Z",
     "shell.execute_reply.started": "2024-08-10T03:54:19.219081Z"
    }
   },
   "outputs": [],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"You are very powerful assistant, but don't know current events\",\n",
    "        ),\n",
    "        (\"user\", \"{input}\"),\n",
    "        MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "e0210633-6dee-4566-ba85-ebd2e2be39ed",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T03:57:50.395262Z",
     "iopub.status.busy": "2024-08-10T03:57:50.394645Z",
     "iopub.status.idle": "2024-08-10T03:57:50.499491Z",
     "shell.execute_reply": "2024-08-10T03:57:50.497783Z",
     "shell.execute_reply.started": "2024-08-10T03:57:50.395215Z"
    }
   },
   "outputs": [],
   "source": [
    "llm = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n",
    "llm_with_tools = llm.bind_tools(tools)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "0674ba75-11ff-43b7-8731-2589f34d94de",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T03:57:52.422574Z",
     "iopub.status.busy": "2024-08-10T03:57:52.422135Z",
     "iopub.status.idle": "2024-08-10T03:57:52.432551Z",
     "shell.execute_reply": "2024-08-10T03:57:52.430605Z",
     "shell.execute_reply.started": "2024-08-10T03:57:52.422543Z"
    }
   },
   "outputs": [],
   "source": [
    "from langchain.agents.format_scratchpad.openai_tools import (\n",
    "    format_to_openai_tool_messages,\n",
    ")\n",
    "from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser\n",
    "\n",
    "# runnable Sequence，根据需要循环执行这 4 步；\n",
    "agent = (\n",
    "    {\n",
    "        \"input\": lambda x: x[\"input\"],\n",
    "        \"agent_scratchpad\": lambda x: format_to_openai_tool_messages(\n",
    "            x[\"intermediate_steps\"]\n",
    "        ),\n",
    "    }\n",
    "    | prompt\n",
    "    | llm_with_tools\n",
    "    | OpenAIToolsAgentOutputParser()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "cad96af0-6beb-4e99-b378-b89df65e139a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T03:57:54.777757Z",
     "iopub.status.busy": "2024-08-10T03:57:54.777101Z",
     "iopub.status.idle": "2024-08-10T03:57:54.786813Z",
     "shell.execute_reply": "2024-08-10T03:57:54.784724Z",
     "shell.execute_reply.started": "2024-08-10T03:57:54.777709Z"
    }
   },
   "outputs": [],
   "source": [
    "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "470c31d3-6739-41fe-92ea-56156245fdca",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T03:58:29.508500Z",
     "iopub.status.busy": "2024-08-10T03:58:29.507862Z",
     "iopub.status.idle": "2024-08-10T03:58:31.672560Z",
     "shell.execute_reply": "2024-08-10T03:58:31.670474Z",
     "shell.execute_reply.started": "2024-08-10T03:58:29.508452Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "Invoking: `get_word_length` with `{'word': 'eudca'}`\n",
      "\n",
      "\n",
      "\u001b[0m\u001b[36;1m\u001b[1;3m5\u001b[0m\u001b[32;1m\u001b[1;3mThe word \"eudca\" has 5 letters.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "process_list = list(agent_executor.stream({\"input\": \"How many letters in the word eudca\"}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "de2bf3b1-4e02-4182-ae8e-4bcaed8e7828",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T04:24:09.688917Z",
     "iopub.status.busy": "2024-08-10T04:24:09.688275Z",
     "iopub.status.idle": "2024-08-10T04:24:09.703005Z",
     "shell.execute_reply": "2024-08-10T04:24:09.700832Z",
     "shell.execute_reply.started": "2024-08-10T04:24:09.688872Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'actions': [ToolAgentAction(tool='get_word_length', tool_input={'word': 'eudca'}, log=\"\\nInvoking: `get_word_length` with `{'word': 'eudca'}`\\n\\n\\n\", message_log=[AIMessageChunk(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_P3LQGFQ50JvKMtLpLXJ2SSsZ', 'function': {'arguments': '{\"word\":\"eudca\"}', 'name': 'get_word_length'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-94428a05-fa61-4853-8eeb-7c12f68b447e', tool_calls=[{'name': 'get_word_length', 'args': {'word': 'eudca'}, 'id': 'call_P3LQGFQ50JvKMtLpLXJ2SSsZ', 'type': 'tool_call'}], tool_call_chunks=[{'name': 'get_word_length', 'args': '{\"word\":\"eudca\"}', 'id': 'call_P3LQGFQ50JvKMtLpLXJ2SSsZ', 'index': 0, 'type': 'tool_call_chunk'}])], tool_call_id='call_P3LQGFQ50JvKMtLpLXJ2SSsZ')],\n",
       "  'messages': [AIMessageChunk(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_P3LQGFQ50JvKMtLpLXJ2SSsZ', 'function': {'arguments': '{\"word\":\"eudca\"}', 'name': 'get_word_length'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-94428a05-fa61-4853-8eeb-7c12f68b447e', tool_calls=[{'name': 'get_word_length', 'args': {'word': 'eudca'}, 'id': 'call_P3LQGFQ50JvKMtLpLXJ2SSsZ', 'type': 'tool_call'}], tool_call_chunks=[{'name': 'get_word_length', 'args': '{\"word\":\"eudca\"}', 'id': 'call_P3LQGFQ50JvKMtLpLXJ2SSsZ', 'index': 0, 'type': 'tool_call_chunk'}])]},\n",
       " {'steps': [AgentStep(action=ToolAgentAction(tool='get_word_length', tool_input={'word': 'eudca'}, log=\"\\nInvoking: `get_word_length` with `{'word': 'eudca'}`\\n\\n\\n\", message_log=[AIMessageChunk(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_P3LQGFQ50JvKMtLpLXJ2SSsZ', 'function': {'arguments': '{\"word\":\"eudca\"}', 'name': 'get_word_length'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-94428a05-fa61-4853-8eeb-7c12f68b447e', tool_calls=[{'name': 'get_word_length', 'args': {'word': 'eudca'}, 'id': 'call_P3LQGFQ50JvKMtLpLXJ2SSsZ', 'type': 'tool_call'}], tool_call_chunks=[{'name': 'get_word_length', 'args': '{\"word\":\"eudca\"}', 'id': 'call_P3LQGFQ50JvKMtLpLXJ2SSsZ', 'index': 0, 'type': 'tool_call_chunk'}])], tool_call_id='call_P3LQGFQ50JvKMtLpLXJ2SSsZ'), observation=5)],\n",
       "  'messages': [FunctionMessage(content='5', name='get_word_length')]},\n",
       " {'output': 'The word \"eudca\" has 5 letters.',\n",
       "  'messages': [AIMessage(content='The word \"eudca\" has 5 letters.')]}]"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "process_list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "83ba25cc-4029-4290-ac88-18da462baee5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T04:07:40.763790Z",
     "iopub.status.busy": "2024-08-10T04:07:40.763155Z",
     "iopub.status.idle": "2024-08-10T04:07:40.775548Z",
     "shell.execute_reply": "2024-08-10T04:07:40.773380Z",
     "shell.execute_reply.started": "2024-08-10T04:07:40.763744Z"
    }
   },
   "outputs": [],
   "source": [
    "from langchain_core.prompts import MessagesPlaceholder\n",
    "\n",
    "MEMORY_KEY = \"chat_history\"\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"You are very powerful assistant, but bad at calculating lengths of words.\",\n",
    "        ),\n",
    "        MessagesPlaceholder(variable_name=MEMORY_KEY),\n",
    "        (\"user\", \"{input}\"),\n",
    "        MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "95945e6b-8541-47ef-a78e-a4f783025168",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T04:07:47.059092Z",
     "iopub.status.busy": "2024-08-10T04:07:47.058455Z",
     "iopub.status.idle": "2024-08-10T04:07:47.067835Z",
     "shell.execute_reply": "2024-08-10T04:07:47.065624Z",
     "shell.execute_reply.started": "2024-08-10T04:07:47.059044Z"
    }
   },
   "outputs": [],
   "source": [
    "from langchain_core.messages import AIMessage, HumanMessage\n",
    "\n",
    "chat_history = []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "2899c0ff-d17c-423d-b329-e1583e310651",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T04:08:02.253848Z",
     "iopub.status.busy": "2024-08-10T04:08:02.253179Z",
     "iopub.status.idle": "2024-08-10T04:08:02.267666Z",
     "shell.execute_reply": "2024-08-10T04:08:02.265862Z",
     "shell.execute_reply.started": "2024-08-10T04:08:02.253798Z"
    }
   },
   "outputs": [],
   "source": [
    "agent = (\n",
    "    {\n",
    "        \"input\": lambda x: x[\"input\"],\n",
    "        \"agent_scratchpad\": lambda x: format_to_openai_tool_messages(\n",
    "            x[\"intermediate_steps\"]\n",
    "        ),\n",
    "        \"chat_history\": lambda x: x[\"chat_history\"],\n",
    "    }\n",
    "    | prompt\n",
    "    | llm_with_tools\n",
    "    | OpenAIToolsAgentOutputParser()\n",
    ")\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "f8c1f32b-9aff-4e3f-9a5a-bc3135332e9e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T04:08:37.889013Z",
     "iopub.status.busy": "2024-08-10T04:08:37.888389Z",
     "iopub.status.idle": "2024-08-10T04:08:41.394099Z",
     "shell.execute_reply": "2024-08-10T04:08:41.392267Z",
     "shell.execute_reply.started": "2024-08-10T04:08:37.888968Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "Invoking: `get_word_length` with `{'word': 'educa'}`\n",
      "\n",
      "\n",
      "\u001b[0m\u001b[36;1m\u001b[1;3m5\u001b[0m\u001b[32;1m\u001b[1;3mThe word \"educa\" has 5 letters.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m\"Educa\" is not a standard English word. It might be a truncated form of \"education\" or a name, but it is not commonly recognized as a standalone word in English.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'is that a real word?',\n",
       " 'chat_history': [HumanMessage(content='how many letters in the word educa?'),\n",
       "  AIMessage(content='The word \"educa\" has 5 letters.')],\n",
       " 'output': '\"Educa\" is not a standard English word. It might be a truncated form of \"education\" or a name, but it is not commonly recognized as a standalone word in English.'}"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "input1 = \"how many letters in the word educa?\"\n",
    "result = agent_executor.invoke({\"input\": input1, \n",
    "                                \"chat_history\": chat_history})\n",
    "chat_history.extend(\n",
    "    [\n",
    "        HumanMessage(content=input1),\n",
    "        AIMessage(content=result[\"output\"]),\n",
    "    ]\n",
    ")\n",
    "agent_executor.invoke({\"input\": \"is that a real word?\", \n",
    "                       \"chat_history\": chat_history})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc5c1efa-26cb-4a31-96f1-d82a6df01d0e",
   "metadata": {},
   "source": [
    "- messages\n",
    "    - system\n",
    "    - human (user)\n",
    "    - ai (assistant)\n",
    "    - human (user)\n",
    "    - ai (assistant)\n",
    "    - ..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "76f76ea0-f40a-47ab-91b2-a0c59ffd817a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-10T04:15:55.208960Z",
     "iopub.status.busy": "2024-08-10T04:15:55.208344Z",
     "iopub.status.idle": "2024-08-10T04:16:00.248743Z",
     "shell.execute_reply": "2024-08-10T04:16:00.246607Z",
     "shell.execute_reply.started": "2024-08-10T04:15:55.208914Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "Invoking: `get_word_length` with `{'word': 'dadebfdr'}`\n",
      "\n",
      "\n",
      "\u001b[0m\u001b[36;1m\u001b[1;3m8\u001b[0m\u001b[32;1m\u001b[1;3mThe word \"dadebfdr\" has 8 letters.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mNo, \"dadebfdr\" does not appear to be a real word in the English language. It seems to be a random string of letters.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'is that a real word?',\n",
       " 'chat_history': [HumanMessage(content='how many letters in the word dadebfdr?'),\n",
       "  AIMessage(content='The word \"dadebfdr\" has 8 letters.')],\n",
       " 'output': 'No, \"dadebfdr\" does not appear to be a real word in the English language. It seems to be a random string of letters.'}"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chat_history = []\n",
    "# https://smith.langchain.com/hub/hwchase17/openai-functions-agent\n",
    "# input_variables=['agent_scratchpad', 'input'], optional_variables=['chat_history']\n",
    "prompt = hub.pull('hwchase17/openai-functions-agent')\n",
    "agent = (\n",
    "    {\n",
    "        \"input\": lambda x: x[\"input\"],\n",
    "        \"agent_scratchpad\": lambda x: format_to_openai_tool_messages(\n",
    "            x[\"intermediate_steps\"]\n",
    "        ),\n",
    "        \"chat_history\": lambda x: x[\"chat_history\"],\n",
    "    }\n",
    "    | prompt\n",
    "    | llm_with_tools\n",
    "    | OpenAIToolsAgentOutputParser()\n",
    ")\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n",
    "input1 = \"how many letters in the word dadebfdr?\"\n",
    "result = agent_executor.invoke({\"input\": input1, \n",
    "                                \"chat_history\": chat_history})\n",
    "chat_history.extend(\n",
    "    [\n",
    "        HumanMessage(content=input1),\n",
    "        AIMessage(content=result[\"output\"]),\n",
    "    ]\n",
    ")\n",
    "agent_executor.invoke({\"input\": \"is that a real word?\", \n",
    "                       \"chat_history\": chat_history})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bf90e817-a466-44c9-8ef0-e53e8b6c2e83",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
